package halftone.errdiff

import chisel3._
import chisel3.util.Counter
import halftone.errdiff.pipeline._
import halftone.{ErrDiffConfig, ErrDiffCore}
import utils.EdgeDetector

object ErrDiffCorePipParam {
  val CycleNr: Int = 3
}

class ErrDiffCorePip(config: ErrDiffConfig) extends ErrDiffCore(config) {
  /*
   * All Five Stages
   * multi-cycle pipeline
   */
  // get pixel from image storage
  val pixelGet = Module(new PixelGetPip(config))

  // get 3 diffused errs around from error cache
  val errorIn = Module(new ErrorInPip(config))

  // calculate, get binary value and four error values(LUT)
  val threshCalc = Module(new ThreshCalcPip(config))

  // output to error cache
  val errorOut = Module(new ErrorOutPip(config))

  // write binary value
  val writeBinary = Module(new WriteBinaryPip(config))

  /*
   * Logics
   */
  // Stages connection
  errorIn.io.in     <> pixelGet.io.out
  threshCalc.io.in  <> errorIn.io.out
  errorOut.io.in    <> threshCalc.io.out
  writeBinary.io.in <> errorOut.io.out
  // Forwarding
  threshCalc.io.err := errorOut.io.err
  errorIn.io.err_r  := threshCalc.io.err_r

  // useless signals
  writeBinary.io.out.ready := true.B

  // IO ports
  io.img.pb   <> pixelGet.io.img // read pixel
  io.img.pa   <> writeBinary.io.img // write pixel
  io.cache.pb <> errorIn.io.pb // get error
  io.cache.pa <> errorOut.io.pa // write error

  // Registers
  val triggered = RegInit(false.B)
  when(io.in.fire) { triggered := true.B }
  val busy        = RegInit(false.B)
  val resultValid = RegInit(false.B)

  /** Store first column error */
  threshCalc.io.err_fc := errorOut.io.err_fc

  io.out.valid := resultValid
  io.in.ready  := !busy

  // pixel position counter
  val (pos, posWrap) = Counter(pixelGet.io.end, config.imageSiz)
  // Execution trigger
  val pipeExe = EdgeDetector(io.in.fire) || pixelGet.io.out.fire
  pixelGet.io.in.valid    := pipeExe && !resultValid
  pixelGet.io.in.bits.pos := pos

  // Stop whole pipeline
  pixelGet.io.stop   := resultValid
  errorIn.io.stop    := resultValid
  threshCalc.io.stop := resultValid
  errorOut.io.stop   := resultValid

  when(busy) {
    when(writeBinary.io.end) {
      resultValid := true.B
    }
    when(io.out.fire) {
      busy        := false.B
      resultValid := false.B
    }
  }.otherwise {
    when(io.in.valid) {
      busy := true.B
    }
  }
}
